/**
 * Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.opsli.modulars.shop.userorder.web;

import java.rmi.ServerError;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import com.alibaba.druid.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.opsli.api.base.result.ResultWrapper;
import org.opsli.api.web.shop.device.SysShopDeviceRestApi;
import org.opsli.api.web.system.org.SysOrgRestApi;
import org.opsli.api.wrapper.shop.device.SysShopDeviceModel;
import org.opsli.api.wrapper.system.org.SysOrgModel;
import org.opsli.common.annotation.ApiRestController;
import org.opsli.common.exception.ServiceException;
import org.opsli.common.utils.WrapperUtil;
import org.opsli.core.base.controller.BaseRestController;
import org.opsli.core.cache.CacheUtil;
import org.opsli.core.persistence.Page;
import org.opsli.core.persistence.querybuilder.QueryBuilder;
import org.opsli.core.persistence.querybuilder.WebQueryBuilder;
import org.opsli.core.utils.DYCUtils;
import org.opsli.core.utils.UserUtil;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.opsli.core.log.enums.*;
import org.opsli.core.log.annotation.OperateLogger;

import org.opsli.modulars.shop.userorder.entity.SysUserOrder;
import org.opsli.api.wrapper.shop.userorder.SysUserOrderModel;
import org.opsli.modulars.shop.userorder.service.ISysUserOrderService;
import org.opsli.api.web.shop.userorder.SysUserOrderRestApi;

/**
 * 用户订单 Controller
 *
 * @author 17607080935
 * @date 2025-01-08 15:52:29
 */
@Api(tags = SysUserOrderRestApi.TITLE)
@Slf4j
@ApiRestController("/{ver}/shop/userorder")
public class SysUserOrderRestController extends BaseRestController<SysUserOrder, SysUserOrderModel, ISysUserOrderService>
    implements SysUserOrderRestApi {
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedisMessageListenerContainer listenerContainer;

    /**
     * 用户订单 查一条
     * @param model 模型
     * @return ResultWrapper
     */
    @ApiOperation(value = "获得单条用户订单", notes = "获得单条用户订单 - ID")
//    @PreAuthorize("hasAuthority('shop_userorder_select')")
    @Override
    public ResultWrapper<SysUserOrderModel> get(SysUserOrderModel model) {
        // 如果系统内部调用 则直接查数据库
        if(model != null && model.getIzApi() != null && model.getIzApi()){
            model = IService.get(model);
        }
        return ResultWrapper.getSuccessResultWrapper(model);
    }

    @Autowired
    SysOrgRestApi sysOrgRestApi;

    @Resource
    SysShopDeviceRestApi sysShopDeviceRestApi ;

    @Override
    public ResultWrapper<?> getById(String id) {
        SysUserOrderModel model = get(id);

        //组装设备信息
//        SysShopDeviceModel sysShopDeviceModel = new   SysShopDeviceModel();
//        sysShopDeviceModel.setId(model.getId());
//        model.setSysShopDeviceModel(sysShopDeviceRestApi.get(sysShopDeviceModel).getData());

        //组装门店信息
        DYCUtils.getOrgIdByOrgIds(model.getOrgIds());
        SysOrgModel sysOrgModel = new SysOrgModel();
        sysOrgModel.setId(DYCUtils.getOrgIdByOrgIds(model.getOrgIds()));
        model.setSysOrgModel(sysOrgRestApi.get(sysOrgModel).getData());
        return ResultWrapper.getSuccessResultWrapper(model);
    }

    @Override
    public List<SysUserOrderModel> getListByDate(Date date,String orgIds,String deviceId) {
        List<Integer> statusList = new ArrayList<>();
        //判断查询的是否是今天。是就获取当前时间。不是就使用一天的开始时间
        boolean isToday = DateUtil.isSameDay(date, new Date());
        Date startTime;
        Date endTime ;

        if (!isToday) {
            startTime = date;
        } else {
            startTime = new Date();
        }
        endTime = DateUtil.offsetHour(startTime, 24);
        QueryWrapper<SysUserOrder> queryWrapper = new QueryWrapper();
        queryWrapper.and(wrapper ->  wrapper.ge("start_time", startTime).le("start_time", endTime).or().ge("end_time", startTime).le("end_time", endTime));
        queryWrapper.eq("org_ids",orgIds);
        queryWrapper.eq("device_id",deviceId);
        statusList.add(1);
        statusList.add(0);
        statusList.add(4);
        queryWrapper.in("status",statusList);
        return WrapperUtil.transformInstance(IService.list(queryWrapper),SysUserOrderModel.class);
    }

    /**
     * 判断订单是否是 已支付、已预约 消费中
     * @param id 订单id
     * @return
     */
    @Override
    public ResultWrapper<?> isPayOrReservation(String id) {

        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("id",id);
        queryWrapper.in("status","1","4");
        queryWrapper.eq("price_status","1");
        long integer = IService.count(queryWrapper);
        if (integer==0) {
            throw new ServiceException(500,"该订单无法开门");
        }
        return null;
    }

    /**
     *  查询可开门的订单
     * @return
     */
    @Override
    public ResultWrapper<SysUserOrderModel> getOpenDoorOrder() {

        //获取当前时间 前后24小时之内
        Date startTime = DateUtil.offsetHour(new Date(), -12);
        Date endTime = DateUtil.offsetHour(new Date(), 12);
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("user_id",UserUtil.getUser().getId());
        queryWrapper.in("status","1","4");
        queryWrapper.ge("start_time", startTime);
        queryWrapper.le("end_time", endTime);
        queryWrapper.eq("price_status","1");
        SysUserOrderModel sysUserOrderModel = WrapperUtil.transformInstance(IService.getOne(queryWrapper),SysUserOrderModel.class);
        if (sysUserOrderModel==null) {
            throw new ServiceException(500,"没有可开门的订单");
        }
        return ResultWrapper.getSuccessResultWrapper(sysUserOrderModel);
    }


    @Override
    public void startOrder(String id) {

        SysUserOrderModel s = IService.get(id);
        if (s==null) {
            throw new ServiceException(500,"该订单不存在");
        }
        if (!"1".equals(s.getPriceStatus())) {
            throw new ServiceException(500,"该订单未支付,无法开门");
        }

        if (!"1".equals(s.getStatus())&&!"4".equals(s.getStatus())) {
            throw new ServiceException(500,"该订单已完成或者取消,无法开门");
        }

        //开始订单
        if ("1".equals(s.getStatus())){
            //判断当前时间 是否大于订单的开始时间 并且小于订单的结束时间 用DateUtil.compare
            if (!(DateUtil.compare(new Date(),s.getStartTime())>0&&DateUtil.compare(s.getEndTime(),new Date())>0)) {
                throw new ServiceException(500,"当前时间不是该订单的开门时间");
            }
            s.setStatus("4");
            IService.updateById(WrapperUtil.transformInstance(s,SysUserOrder.class));

            String key = "order#startOrder:" + id;

            //订单5分钟未支付 将删除
            redisTemplate.opsForValue().set(key, id, s.getHourNumber()*3600, TimeUnit.SECONDS);
            addExpireListener(key,2);
        }



    }

    /**
     * 用户订单 查询分页
     * @param pageNo 当前页
     * @param pageSize 每页条数
     * @param request request
     * @return ResultWrapper
     */
    @ApiOperation(value = "获得分页数据", notes = "获得分页数据 - 查询构造器")
//    @PreAuthorize("hasAuthority('shop_userorder_select')")
    @Override
    public ResultWrapper<?> findPage(Integer pageNo, Integer pageSize, HttpServletRequest request) {

        QueryBuilder<SysUserOrder> queryBuilder = new WebQueryBuilder<>(IService.getEntityClass(), request.getParameterMap());
        Page<SysUserOrder, SysUserOrderModel> page = new Page<>(pageNo, pageSize);
        page.setQueryWrapper(queryBuilder.build());
        page = IService.findPage(page);
        SysShopDeviceModel sysShopDeviceModel = new SysShopDeviceModel();
        SysOrgModel sysOrgModel = new SysOrgModel();
        for (SysUserOrderModel model :  page.getList()) {


            //如果跟上一个一样 那就不用再查询了
            if (!(sysShopDeviceModel.getId()!=null&&sysShopDeviceModel.getId().equals(model.getDeviceId()))) {
                sysShopDeviceModel.setId(model.getDeviceId());
                sysShopDeviceModel.setIzApi(true);
                sysShopDeviceModel = sysShopDeviceRestApi.get(sysShopDeviceModel).getData();
            }

            model.setSysShopDeviceModel(sysShopDeviceModel);
            if (!(sysOrgModel.getId()!=null&&sysOrgModel.getId().equals(DYCUtils.getOrgIdByOrgIds(model.getOrgIds())))) {
                sysOrgModel.setIzApi(true);
                sysOrgModel.setId(DYCUtils.getOrgIdByOrgIds(model.getOrgIds()));
                sysOrgModel = sysOrgRestApi.get(sysOrgModel).getData();
            }
            model.setSysOrgModel(sysOrgModel);
        }

        return ResultWrapper.getSuccessResultWrapper(page.getPageData());
    }

    /**
     * 用户订单 新增
     * @param model 模型
     * @return ResultWrapper
     */
    @ApiOperation(value = "新增用户订单数据", notes = "新增用户订单数据")
//    @PreAuthorize("hasAuthority('shop_userorder_insert')")
    @OperateLogger(description = "新增用户订单数据",
            module = ModuleEnum.MODULE_UNKNOWN, operationType = OperationTypeEnum.INSERT, db = true)
    @Override
    @Transactional
    public ResultWrapper<?> insert(SysUserOrderModel model) {

        //检查订单是否合规
        checkOrder(model);
        model.setEndTime(DateUtil.offsetHour(model.getStartTime(), model.getHourNumber()));
        model.setUserId(UserUtil.getUser().getId());
        // 调用新增方法
        model = IService.insert(model);

        String key = "order#unpaid:" + model.getId();

        //订单5分钟未支付 将删除
        redisTemplate.opsForValue().set(key, model.getId(), 300, TimeUnit.SECONDS);
        addExpireListener(key,1);

        return ResultWrapper.getCustomResultWrapper(model,200,"创建订单成功");
    }

    /**
     * 添加过期监听器
     * @Param key key
     * @Param type 类型1 未支付倒计时，2消费倒计时
     */
    private void addExpireListener(String key,Integer type) {
        String channelPattern = "__keyevent@*__:expired";
        log.info("订单 {} 开始监听过期", key);
        MessageListener listener = (message, pattern) -> {
            String expiredKey = new String(message.getBody());
            if (expiredKey.equals(key)) {
                // 在这里处理过期事件
                if (type==1) {
                    //未支付倒计时
                    deleteExpiredOrder(expiredKey);
                }
                if (type==2) {
                    //消费倒计时


                    handleExpired(expiredKey);
                }

            }
        };

        listenerContainer.addMessageListener(
                listener,
                new PatternTopic(channelPattern)
        );
    }


    /**
     * 删除过期订单
     * @Param key key
     */

    private void deleteExpiredOrder(String key) {
        String id = key.split(":")[1];
        SysUserOrderModel sysUserOrderModel = IService.get(id);
        if ("0".equals(sysUserOrderModel.getPriceStatus())) {
            //如果订单未支付 则删除订单
            IService.delete(id);
            // 在这里处理过期事件
            log.info("订单 {} 过期已删除", key);
        }

    }

    /**
     * 消费结束
     */
    private void handleExpired(String key) {
        String id = key.split(":")[1];
        SysUserOrderModel sysUserOrderModel = IService.get(id);
        if ("4".equals(sysUserOrderModel.getStatus())) {
            //如果订单未支付 则删除订单
            UpdateWrapper u = new UpdateWrapper();
            u.set("status", "2");
            IService.update(null,u);
            // 在这里处理过期事件
            log.info("订单 {} 已结束", key);

            sysShopDeviceRestApi.closeDevice(sysUserOrderModel.getDeviceId());
        }

    }

    @Override
    @Transactional
    public ResultWrapper<?> reservation(SysUserOrderModel model) {

        if (model == null||StringUtils.isEmpty(model.getId())) {
            return ResultWrapper.getCustomResultWrapper(null,500,"参数不能为空");
        }

        checkOrder(model);

        //支付方法 由回调通知处理
        paySuccess(model.getId());
        //预约前检查是否符合要求
        return ResultWrapper.getSuccessResultWrapperByMsg("预约成功");
    }



    @Override
    @Transactional
    public ResultWrapper<?> paySuccess(String orderId) {
        IService.paySuccess(orderId);
        //预约前检查是否符合要求
        return null;
    }

    /**
     * 检查该订单符合预约要求 不符合要求会自己报错
     *
     */

    public void checkOrder(SysUserOrderModel model){
        //新增时 不用验证订单状态

        //判断订单是否已支付 订单必须是 未支付 状态才可继续

        if (StringUtils.isEmpty(model.getId())) {

        }else {
            model = WrapperUtil.transformInstance(IService.getById(model.getId()), SysUserOrderModel.class);
            //判断订单状态是否 是未支付
            if (!"0".equals(model.getStatus())) {
                throw new ServiceException(500,"该订单已支付");

            }


        }
        //判断时间是否冲突

        //检查该用户是否有卷

        //检查该设备是否可用

        //设置结束时间

    }

    /**
     * 用户订单 修改
     * @param model 模型
     * @return ResultWrapper
     */
    @ApiOperation(value = "修改用户订单数据", notes = "修改用户订单数据")
    @PreAuthorize("hasAuthority('shop_userorder_update')")
    @OperateLogger(description = "修改用户订单数据",
            module = ModuleEnum.MODULE_UNKNOWN, operationType = OperationTypeEnum.UPDATE, db = true)
    @Override
    public ResultWrapper<?> update(SysUserOrderModel model) {
        // 调用修改方法
        IService.update(model);
        return ResultWrapper.getSuccessResultWrapperByMsg("修改用户订单成功");
    }


    /**
     * 用户订单 删除
     * @param id ID
     * @return ResultVo
     */
    @ApiOperation(value = "删除用户订单数据", notes = "删除用户订单数据")
    @PreAuthorize("hasAuthority('shop_userorder_delete')")
    @OperateLogger(description = "删除用户订单数据",
            module = ModuleEnum.MODULE_UNKNOWN, operationType = OperationTypeEnum.DELETE, db = true)
    @Override
    public ResultWrapper<?> del(String id){
        IService.delete(id);
        return ResultWrapper.getSuccessResultWrapperByMsg("删除用户订单成功");
    }

    /**
     * 用户订单 批量删除
     * @param ids ID 数组
     * @return ResultVo
     */
    @ApiOperation(value = "批量删除用户订单数据", notes = "批量删除用户订单数据")
    @PreAuthorize("hasAuthority('shop_userorder_delete')")
    @OperateLogger(description = "批量删除用户订单数据",
            module = ModuleEnum.MODULE_UNKNOWN, operationType = OperationTypeEnum.DELETE, db = true)
    @Override
    public ResultWrapper<?> delAll(String ids){
        String[] idArray = Convert.toStrArray(ids);
        IService.deleteAll(idArray);
        return ResultWrapper.getSuccessResultWrapperByMsg("批量删除用户订单成功");
    }

    /**
     * 用户订单 Excel 导出认证
     *
     * @param type 类型
     * @param request request
     */
    @ApiOperation(value = "Excel 导出认证", notes = "Excel 导出认证")
    @PreAuthorize("hasAnyAuthority('shop_userorder_export', 'shop_userorder_import')")
    @Override
    public ResultWrapper<String> exportExcelAuth(String type, HttpServletRequest request) {
        Optional<String> certificateOptional =
                super.excelExportAuth(type, SysUserOrderRestApi.SUB_TITLE, request);
        if(!certificateOptional.isPresent()){
            return ResultWrapper.getErrorResultWrapper();
        }
        return ResultWrapper.getSuccessResultWrapper(certificateOptional.get());
    }


    /**
     * 用户订单 Excel 导出
     * @param response response
     */
    @ApiOperation(value = "导出Excel", notes = "导出Excel")
    @PreAuthorize("hasAuthority('shop_userorder_export')")
    @OperateLogger(description = "用户订单 导出Excel",
            module = ModuleEnum.MODULE_UNKNOWN, operationType = OperationTypeEnum.SELECT, db = true)
    @Override
    public void exportExcel(String certificate, HttpServletResponse response) {
        // 导出Excel
        super.excelExport(certificate, response);
    }

    /**
     * 用户订单 Excel 导入
     * 注：这里 RequiresPermissions 引入的是 Shiro原生鉴权注解
     * @param request 文件流 request
     * @return ResultVo
     */
    @ApiOperation(value = "导入Excel", notes = "导入Excel")
    @PreAuthorize("hasAuthority('shop_userorder_import')")
    @OperateLogger(description = "用户订单 Excel 导入",
            module = ModuleEnum.MODULE_UNKNOWN, operationType = OperationTypeEnum.INSERT, db = true)
    @Override
    public ResultWrapper<?> importExcel(MultipartHttpServletRequest request) {
        return super.importExcel(request);
    }

}